import threading
import logging
from collections import deque
import time

import globalSettings

class Enum(set):
    def __getattr__(self, name):
        if name in self:
            return name
        raise AttributeError
    
class WvongAlgorithm(threading.Thread):
    def __init__(self, inputQueue, inputQueueLock, fsm):
        threading.Thread.__init__(self)
        
        self.inputQueue = inputQueue
        self.inputQueueLock = inputQueueLock
        self.fromIutQueue = deque([], maxlen = globalSettings.FROM_IUT_DELTA/globalSettings.TIME_QUANT)
        self.fromIutQueue.extend([None] * self.fromIutQueue.maxlen)
        self.fromIutNumberOfMessages = 0
        
        self.toIutQueue = deque([], maxlen = globalSettings.TO_IUT_DELTA/globalSettings.TIME_QUANT)
        self.toIutQueue.extend([None] * self.toIutQueue.maxlen)
        self.toIutNumberOfMessages = 0
        
        self.statesQueue = deque([], maxlen = globalSettings.FROM_IUT_DELTA/globalSettings.TIME_QUANT)
        self.statesQueue.extend([None] * self.statesQueue.maxlen)
        
        self.fsm = fsm
        
        self.lastTakenStateSet = self.fsm.states
        self.fromIutQueueIndex = 0
        self.toIutQueueIndex = 0
        
        #self.verdict = Enum (["NONE", "PASS", "FAIL"])
        self.verdict = "PASS"
        self.totalSimTime = 0
   
    def updateStatesForInputMessage(self, message):
        newPossibleStates = self.fsm.nextPossibleStates(self.lastTakenStateSet, message.produceSymbol())
        #if len(newPossibleStates) > 0:
        #    self.lastTakenStateSet = newPossibleStates
        self.lastTakenStateSet = newPossibleStates

        for i in range(1, self.statesQueue.maxlen):
            localFromIutIndex = (self.fromIutQueueIndex + i) % (globalSettings.FROM_IUT_DELTA/globalSettings.TIME_QUANT)
            if self.fromIutQueue[localFromIutIndex] != None:
                newPossibleStates1 = self.fsm.nextPossibleStates(newPossibleStates, self.fromIutQueue[localFromIutIndex].produceSymbol())
                newPossibleStates2 = self.fsm.nextPossibleStates(self.statesQueue[localFromIutIndex], message.produceSymbol())
                newPossibleStates = newPossibleStates1.union(newPossibleStates2)
                
                #if len(newPossibleStates) > 0:
                #    self.statesQueue[localFromIutIndex] = newPossibleStates
                self.statesQueue[localFromIutIndex] = newPossibleStates

    def updateStatesForOutputMessage(self, message):
        previousStateSet = self.lastTakenStateSet
        for i in range(self.statesQueue.maxlen-1):
            if self.statesQueue[self.fromIutQueueIndex - i] != None:
                previousStateSet = self.statesQueue[self.fromIutQueueIndex - i]
                break
        
        newPossibleStates = self.fsm.nextPossibleStates(previousStateSet, message.produceSymbol())
        self.statesQueue[self.fromIutQueueIndex] = newPossibleStates
   
    def updateStates(self, message):
        logging.info('updating states with ' + str(message))
        if message.isOutput:
            self.updateStatesForOutputMessage(message)
        else:
            self.updateStatesForInputMessage(message)
                    
    def checkVerdict(self):
        self.verdict = "FAIL"
        if self.lastTakenStateSet != None and len(self.lastTakenStateSet) != 0:
            self.verdict = "PASS"
            
        for states in self.statesQueue:
            if states != None and len(states) != 0:
                self.verdict = "PASS"
                break
        #print self.verdict
    
    def processReceivedMessage(self, receivedMessage):
        if receivedMessage.isOutput:
            logging.info('put to fromIutQueue on position ' + str(self.fromIutQueueIndex) + ' ' + str(receivedMessage))
            self.fromIutQueue[self.fromIutQueueIndex] = receivedMessage
            self.fromIutNumberOfMessages += 1
            logging.info('updating states ' + str(receivedMessage))
            self.updateStates(receivedMessage)
            self.checkVerdict()
            
        else:
            logging.info('put to toIutQueue on position ' + str(self.fromIutQueueIndex) + ' ' + str(receivedMessage))
            self.toIutQueue[self.toIutQueueIndex] = receivedMessage
            self.toIutNumberOfMessages += 1
        
    def processFromIutQueue(self):
        self.fromIutQueue[self.fromIutQueueIndex] = None
        self.fromIutNumberOfMessages -= 1
        self.lastTakenStateSet = self.statesQueue[self.fromIutQueueIndex]
        self.statesQueue[self.fromIutQueueIndex] = None
        self.checkVerdict()
        
    def processToIutQueue(self):
        self.updateStates(self.toIutQueue[self.toIutQueueIndex])       
        logging.info('take down message ' + str(self.toIutQueue[self.toIutQueueIndex]))
        self.toIutQueue[self.toIutQueueIndex] = None
        self.toIutNumberOfMessages -= 1
        self.checkVerdict()
    
    def displayInfo(self):
        logging.info('FROM IUT ' + str(self.fromIutQueue))
        logging.info('TO IUT ' + str(self.toIutQueue))
        logging.info('STATES ' + str(self.statesQueue))
        logging.info('LAST TAKEN ' + str(self.lastTakenStateSet))
        logging.info('')
        
    def run(self):
        logging.warning("Wvong Algorithm started execution")
        simTime = 0
        #while simTime < globalSettings.SIMULATION_TIME:
        
        stopCondition = True
        while stopCondition and (len(self.inputQueue) != 0 or self.fromIutNumberOfMessages > 0 or self.toIutNumberOfMessages > 0):
            logging.info("simTime: " + str(simTime))
            #self.displayInfo()
            
#            #===================================================================
#            if len(self.inputQueue) == 0 and self.fromIutNumberOfMessages == 0 and self.toIutNumberOfMessages == 0:
#                self.inputQueueLock.acquire()
#                self.inputQueueLock.wait()
#                self.inputQueueLock.release()
#            #===================================================================

            if len(self.inputQueue) > 0:
                if self.inputQueue[0].timeObserved <= simTime:
                    self.inputQueueLock.acquire()
                    receivedMessage = self.inputQueue.popleft()
                    logging.info('processing ' + str(receivedMessage))
                    self.inputQueueLock.release()
                    self.processReceivedMessage(receivedMessage)
                    self.displayInfo()
            
            self.fromIutQueueIndex = (self.fromIutQueueIndex + 1) % (globalSettings.FROM_IUT_DELTA/globalSettings.TIME_QUANT)
            self.toIutQueueIndex = (self.toIutQueueIndex + 1) % (globalSettings.TO_IUT_DELTA/globalSettings.TIME_QUANT)

            if self.fromIutQueue[self.fromIutQueueIndex] != None:
                logging.info('take down message ' + str(self.fromIutQueue[self.fromIutQueueIndex]))
                self.processFromIutQueue()
                self.displayInfo()
            
            if self.toIutQueue[self.toIutQueueIndex] != None:
                self.processToIutQueue()
                self.displayInfo()  


            time.sleep(globalSettings.TIME_QUANT/1000)
            
            if globalSettings.STEP_BY_STEP == True:
                raw_input("Press any key to continue...")
                
            simTime = simTime + globalSettings.TIME_QUANT
            if (globalSettings.STOP_WHEN_FAILED == True):
                stopCondition = self.verdict != "FAIL"
            else:
                stopCondition = True
                
            if (globalSettings.TIME_CONSTRAINED_SIMULATION == True):
                stopCondition = (simTime < globalSettings.SIMULATION_TIME)
                
        logging.warning("Testing End")
        self.totalSimTime = simTime
        if self.fromIutNumberOfMessages != 0 or self.toIutNumberOfMessages != 0:
            raise Exception("Tester is not empty!")

    def __repr__(self):
        stateSet = "Zbior mozliwych stanow: " + str(self.lastTakenStateSet)
        verdict = "Werdykt: " + self.verdict
        return stateSet + '\n' + verdict
    
class WvongAlgorithmMessageOrders(WvongAlgorithm):
    def __init__(self, inputQueue, inputQueueLock, fsm):
        super(WvongAlgorithmMessageOrders, self).__init__(inputQueue, inputQueueLock, fsm)
            
        self.messageOrders = deque([], maxlen = globalSettings.FROM_IUT_DELTA/globalSettings.TIME_QUANT)
        self.messageOrders.extend([None] * self.messageOrders.maxlen)
            
        self.lastTakenMessageOrders = [[]]
    
    def copyMessageOrderList(self, messageOrderList):
        copiedList = []
        for messageList in messageOrderList:
            copiedList.append(messageList[:])
        return copiedList
    
    def updateMessageOrdersForOutputMessage(self, message):
        previousMessageOrders = self.copyMessageOrderList(self.lastTakenMessageOrders)
        for i in range(1, self.messageOrders.maxlen):
            if self.messageOrders[self.fromIutQueueIndex - i] != None:
                previousMessageOrders = self.copyMessageOrderList(self.messageOrders[self.fromIutQueueIndex - i])
                break
        
        newMesageOrders = previousMessageOrders
        for messageOrder in newMesageOrders:
            messageOrder.append(message.produceSymbol())
        self.messageOrders[self.fromIutQueueIndex] = newMesageOrders
        
    def updateMessageOrdersForInputMessage(self, message):
        for messageOrder in self.lastTakenMessageOrders:
            messageOrder.append(message.produceSymbol())
        newPossibleMessageOrders = self.copyMessageOrderList(self.lastTakenMessageOrders)
        
        for i in range(1, self.messageOrders.maxlen):
            if self.fromIutQueue[(self.fromIutQueueIndex + i) % (globalSettings.FROM_IUT_DELTA/globalSettings.TIME_QUANT)] != None:
                newPossibleMessageOrders1 = self.copyMessageOrderList(newPossibleMessageOrders)
                for messageOrder in newPossibleMessageOrders1:
                    messageOrder.append(self.fromIutQueue[(self.fromIutQueueIndex + i) % (globalSettings.FROM_IUT_DELTA/globalSettings.TIME_QUANT)].produceSymbol())
                
                newPossibleMessageOrders2 = self.messageOrders[(self.fromIutQueueIndex + i) % (globalSettings.FROM_IUT_DELTA/globalSettings.TIME_QUANT)][:]
                for messageOrder in newPossibleMessageOrders2:
                    messageOrder.append(message.produceSymbol())
                
                newPossibleMessageOrders = newPossibleMessageOrders1 + newPossibleMessageOrders2
                self.messageOrders[(self.fromIutQueueIndex + i) % (globalSettings.FROM_IUT_DELTA/globalSettings.TIME_QUANT)] = newPossibleMessageOrders
    
    def updateStates(self, message):
        logging.info('updating states with ' + str(message))
        if message.isOutput:
            self.updateStatesForOutputMessage(message)
            self.updateMessageOrdersForOutputMessage(message)
        else:
            self.updateStatesForInputMessage(message)
            self.updateMessageOrdersForInputMessage(message)
        
    def displayInfo(self):
        logging.info('FROM IUT ' + str(self.fromIutQueue))
        logging.info('TO IUT ' + str(self.toIutQueue))
        logging.info('STATES ' + str(self.statesQueue))
        logging.info('LAST TAKEN ' + str(self.lastTakenStateSet))
        logging.info('MESSAGES ' + str(self.messageOrders))
        logging.info('LAST TAKEN MESSAGE' + str(self.lastTakenMessageOrders))
        logging.info('')
        

    def processFromIutQueue(self):
        super(WvongAlgorithmMessageOrders, self).processFromIutQueue()

        self.lastTakenMessageOrders = self.messageOrders[self.fromIutQueueIndex]
        self.messageOrders[self.fromIutQueueIndex] = None
        
    def processToIutQueue(self):
        super(WvongAlgorithmMessageOrders, self).processToIutQueue()
    
    def __repr__(self):
        traces = "Przeanalizowane slady:\n"
        for messageOrder in self.lastTakenMessageOrders:
            traces += str(messageOrder) + '\n'
        traces += "Razem: " + str(len(self.lastTakenMessageOrders)) + '\n'
        return traces + super(WvongAlgorithmMessageOrders, self).__repr__()
    
